/*
 * Copyright 2000-2006 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.intellij.gwt.make;

import com.intellij.execution.process.OSProcessHandler;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.compiler.CompileContext;
import com.intellij.openapi.compiler.CompilerMessageCategory;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.gwt.GwtBundle;

import java.io.IOException;
import java.util.Set;
import java.util.HashSet;
import java.util.Arrays;

import org.jetbrains.annotations.NonNls;

/**
 * @author nik
*/
public class GwtCompilerProcessHandler extends OSProcessHandler {
  private static final Logger LOG = Logger.getInstance("#com.intellij.gwt.make.GwtCompilerProcessHandler");
  @NonNls private static final String ANALYSING_SOURCES_PREFIX = "Analyzing source";
  @NonNls private static final String COPYING_PUBLIC_FILES_PREFIX = "Copying all files found on public path";
  @NonNls private static final String COMPILATION_START_PREFIX = "Output will be written into";
  @NonNls private static final String LOADING_INHERITED_PREFIX = "Loading inherited module";
  @NonNls private static final String FINDING_ENTRY_POINTS_PREFIX = "Finding entry point classes";
  @NonNls private static final String ERROR_PREFIX = "[ERROR] ";
  @NonNls private static final String ERROR_FILE_PREFIX = "Errors in ";
  @NonNls private static final String ERROR_LINE_PREFIX = "Line ";
  @NonNls private static final String ERROR_LINE_SUFFIX = ":  ";
  @NonNls private static final String INVALID_FILE_URL_PREFIX = "file:/";
  @NonNls private static final String VALID_FILE_URL_PREFIX = "file://";
  @NonNls private static final String BUILD_FAILED_MESSAGE = "Build failed";
  @NonNls private static final Set<String> MODULE_FILE_ERRORS = new HashSet<String>(Arrays.asList(
    "Module has no entry points defined"
  ));

  private StringBuilder myOutput = new StringBuilder();
  private CompileContext myContext;
  private String myModuleFileUrl;
  private String myCurrentFileUrl;
  private boolean myFindingEntryPoints = false;

  public GwtCompilerProcessHandler(ProcessBuilder processBuilder, final CompileContext context, final String moduleFileUrl) throws IOException {
    super(processBuilder.start(), "");
    myContext = context;
    myModuleFileUrl = moduleFileUrl;
  }

  public void notifyTextAvailable(final String text, final Key outputType) {
    super.notifyTextAvailable(text, outputType);
    myOutput.append(text);
    parseOutput();
  }

  private void parseOutput() {
    int start = 0;
    while (true) {
      int lineEnd1 = myOutput.indexOf("\n", start);
      int lineEnd2 = myOutput.indexOf("\r", start);
      if (lineEnd1 == -1 && lineEnd2 == -1) break;

      int lineEnd = lineEnd1 == -1 ? lineEnd2 : lineEnd2 == -1 ? lineEnd1 : Math.min(lineEnd1, lineEnd2);
      parseLine(myOutput.substring(start, lineEnd).trim());
      start = lineEnd + 1;
    }

    myOutput.delete(0, start);
  }

  private void parseLine(String line) {
    if (line.length() == 0) return;

    if (LOG.isDebugEnabled()) {
      LOG.debug(line);
    }

    if (line.startsWith(ERROR_FILE_PREFIX)) {
      myCurrentFileUrl = line.substring(ERROR_FILE_PREFIX.length());
      fixFileUrl();
    }
    else if (line.startsWith(ERROR_PREFIX)) {
      boolean errorLineParsed = false;
      int start = ERROR_PREFIX.length();
      if (line.startsWith(ERROR_FILE_PREFIX, start)) {
        start += ERROR_FILE_PREFIX.length();
        int first = line.indexOf('\'', start);
        int last = line.lastIndexOf('\'');
        if (first != -1 && last != -1) {
          myCurrentFileUrl = line.substring(first + 1, last);
          errorLineParsed = true;
          fixFileUrl();
        }
      }
      else if (line.startsWith(ERROR_LINE_PREFIX, start)) {
        start += ERROR_LINE_PREFIX.length();
        final int end = line.indexOf(ERROR_LINE_SUFFIX, start);
        if (end != -1) {
          try {
            int lineNumber = Integer.parseInt(line.substring(start, end));
            String message = line.substring(end + ERROR_LINE_SUFFIX.length());
            myContext.addMessage(CompilerMessageCategory.ERROR, message, myCurrentFileUrl, lineNumber, 0);
            errorLineParsed = true;
          }
          catch (NumberFormatException e) {
          }
        }
      }
      else {
        line = line.substring(start);
      }

      if (MODULE_FILE_ERRORS.contains(line) || myFindingEntryPoints) {
        myContext.addMessage(CompilerMessageCategory.ERROR, line, myModuleFileUrl, -1, -1);
        errorLineParsed = true;
      }

      if (!errorLineParsed && !BUILD_FAILED_MESSAGE.equals(line)) {
        myContext.addMessage(CompilerMessageCategory.ERROR, line, null, -1, -1);
      }
    }
    else if (line.startsWith(ANALYSING_SOURCES_PREFIX)) {
      myContext.getProgressIndicator().setText(GwtBundle.message("progress.text.analyzing.sources"));
    }
    else if (line.startsWith(COPYING_PUBLIC_FILES_PREFIX)) {
      myContext.getProgressIndicator().setText(GwtBundle.message("progress.text.copying.files.from.public.paths"));
    }
    else if (line.startsWith(COMPILATION_START_PREFIX)) {
      myContext.getProgressIndicator().setText(GwtBundle.message("progress.text.compiling.sources"));
    }
    else if (line.startsWith(LOADING_INHERITED_PREFIX)) {
      myContext.getProgressIndicator().setText(GwtBundle.message("progress.text.loading.inherited.modules"));
    }
    else if (line.startsWith(FINDING_ENTRY_POINTS_PREFIX)) {
      myFindingEntryPoints = true;
    }
    else {
      myFindingEntryPoints = false;
    }
  }

  private void fixFileUrl() {
    if (!myCurrentFileUrl.startsWith(VALID_FILE_URL_PREFIX)) {
      if (myCurrentFileUrl.startsWith(INVALID_FILE_URL_PREFIX)) {
        myCurrentFileUrl = VALID_FILE_URL_PREFIX + myCurrentFileUrl.substring(INVALID_FILE_URL_PREFIX.length());
      }
      else {
        myCurrentFileUrl = VALID_FILE_URL_PREFIX + FileUtil.toSystemIndependentName(myCurrentFileUrl);
      }
    }
  }
}
